#include <sstream>

#include "Cats.h"

using namespace std;

// This returns a flag indicating whether dNAP is the problem that should be solved.
//
bool read_cost_benefits(istream& in,  map<string,vector<double>*>& cb,  int& C)
{
	bool dNAP = false;

	C = -1;
	in >> C;
	if (C < 0) throw string("invalid or missing budget in .cb file");

	string line, key;

	while (getline(in, line))
    {
		stringstream ssline(line);
		if (!(ssline >> key)) continue; // this skips white lines and others

		if (cb[key] != 0) 
			throw string("taxon '" +key+ "' is appearing twice in .cb file");

		cb[key] = new vector<double>;

		double first;  // Comments start with a string not followed by a number.
		if(!(ssline >> first)) 
		{
			cout << "Warning: assuming that everything after '" + key + 
				"' is a comment (in the .cb file)\n" << endl;
			return dNAP;
		}

		if (first > 0.0 && first < 1.0)            // Format (2)
		{
			dNAP = true;
			cb[key]->push_back(first);
			append_probabilities(ssline, *cb[key], C+1);
		}

		if (first > 1.0)
		{
			int c = static_cast<int>(first);

			if (c != first) 
				throw string("non-integer cost in .cb file (taxon '"+key+"')");

			double a, b;
			if (!(ssline >> a))                    // Format (1)
				for(int i = 0; i <= C; i++)
					cb[key]->push_back(i >= c ? 1 : 0);
			else                                   
			{                                      // Format (3)
				dNAP = true;
				finish_reading_format3(c, a, ssline, *cb[key], C+1, key);
			}
		}

		if (first == 0.0 || first == 1.0)
		{
			double second;
			
			if (!(ssline >> second))               // Format (1)
				for(int i = 0; i <= C; i++)
					cb[key]->push_back(i >= first ? 1 : 0);
			else                                
			{
				dNAP = true;

				if (second < 0.0 || second > 1.0) 
					throw string(".cb file, taxon '" + key + "': second field"
						" is not a probability");

				if (first == 0.0 || second == 1.0) // Format (2)
				{
					cb[key]->push_back(first);
					cb[key]->push_back(second);
					append_probabilities(ssline, *cb[key], C+1);
				}
				else                               // Format (3)
					finish_reading_format3(1, second, ssline, *cb[key], C+1, key);
					// Line has format <taxon> 1 <a> ... with 0 <= a < 1.		                              
			}
		}

		// Check you've read everyithng there was to read:
		string last;
		if (ssline >> last)
			throw string("unexpected token '" + last + "' in .cb file (taxon '"
				+ key + "')");

	}  // End loop through lines.

	return dNAP;
}


void finish_reading_format3(int c, double a, istream& in, vector<double>& vd, 
							const int size, string key)
{
	double b;
	if (!(in >> b))
		throw string("cost followed by single number in .cb file (taxon '"
			+ key + "')");

	if (a < 0.0 || a > 1.0 || b < 0.0 || b > 1.0 || a > b)
		throw string("invalid format (3) line in .cb file (taxon '" + key + "').");

	for(int i = 0; i < size; i++)
		if (i < c) 
			vd.push_back(a + (b-a)*i/c);
		else 
			vd.push_back(b);
}


// All the probabilities in input are appended to the vector 'vd', until making it
// at least 'min_size' large.  If the resulting vector has less than 'min_size' 
// probabilities, the rest is filled with the probability at the back of 'vd'.
//
void append_probabilities(istream& in, vector<double>& vd, const int min_size)
{
	double p;
	while(in >> p)
		if (p < 0.0 || p > 1.0) 
			throw string("a number not in [0,1] was found "
			             "where a probability was expected");
		else if (vd.size() > 0 && p < vd.back()) 
			throw string("decreasing consecutive probabilities");
		else vd.push_back(p);

	if (vd.size() == 0)
		throw string("append_probabilities cannot fill its vector");

	while(vd.size() < min_size)
		vd.push_back(vd.back());
}


// This function reads an identifier from 'in' to 's'...
// if nothing is read, then s is set to "".
//
void get_id(istream& in, string& s)
{
	char c;

	s = "";
	while(in.get(c) && (isalnum(c) || c == '_'))
		s = s + c; // inefficient?

	if(!in.fail()) in.putback(c);
}
